#include <glib.h>
#include <gio/gio.h>
#include <string.h>

#define EXAMPLE_SERVICE_NAME "CYG.API"
#define EXAMPLE_OBJECT_PATH "/Simples/Example"

// XML interface definition
static const gchar *introspection_xml =
    "<node>"
    "  <interface name='Simples.Example'>"
    "    <method name='SayHello'>"
    "      <arg type='s' direction='in' name='name'/>"
    "      <arg type='s' direction='out' name='response'/>"
    "    </method>"
    "    <method name='AddNumbers'>"
    "      <arg type='i' direction='in' name='a'/>"
    "      <arg type='i' direction='in' name='b'/>"
    "      <arg type='i' direction='out' name='sum'/>"
    "    </method>"
    "    <signal name='ServiceSignal'>"
    "      <arg type='s' name='message'/>"
    "    </signal>"
    "  </interface>"
    "</node>";

static GDBusNodeInfo *introspection_data = NULL;

static void handle_method_call(GDBusConnection *connection,
                               const gchar *sender,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *method_name,
                               GVariant *parameters,
                               GDBusMethodInvocation *invocation,
                               gpointer user_data);

static gboolean emit_signal(gpointer user_data);

static const GDBusInterfaceVTable interface_vtable = {
    handle_method_call,     // method call
    NULL,                   // property setter
    NULL                    // property getter
};

int main()
{
    GError *error = NULL;
    GDBusConnection *connection;
    GMainLoop *loop;
    guint registration_id;

    loop = g_main_loop_new(NULL, FALSE);

    // connect dbus
    connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
    if (connection == NULL) {
        g_printerr("Failed to connect to D-Bus: %s\n", error->message);
        g_error_free(error);
        return -1;
    }

    // Parse the interface from XML
    introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, &error);
    if (introspection_data == NULL) {
        g_printerr("Failed to parse introspection XML: %s\n", error->message);
        g_error_free(error);
        g_object_unref(connection);
        return -1;
    }

    // register object
    registration_id = g_dbus_connection_register_object(connection,
                                      EXAMPLE_OBJECT_PATH,
                                      introspection_data->interfaces[0],
                                      &interface_vtable,
                                      NULL,  // user_data
                                      NULL,  // user_data_free_func
                                      &error);

    if (error != NULL) {
        g_printerr("Failed to register object: %s\n", error->message);
        g_error_free(error);
        g_object_unref(connection);
        g_dbus_node_info_unref(introspection_data);
        return -2;
    }

    // get connection name
    guint owner_id = g_bus_own_name_on_connection(connection,
                                                  EXAMPLE_SERVICE_NAME,
                                                  G_BUS_NAME_OWNER_FLAGS_NONE,
                                                  NULL,  // on_name_acquired
                                                  NULL,  // on_name_lost
                                                  NULL,  // user_data
                                                  NULL); // user_data_free_func

    g_print("D-Bus service '%s' is running on object path '%s'\n", 
            EXAMPLE_SERVICE_NAME, EXAMPLE_OBJECT_PATH);
    g_print("Press Ctrl+C to exit.\n");

    // Pass connection as user_data
    g_timeout_add_seconds(5, (GSourceFunc)emit_signal, connection);

    g_main_loop_run(loop);
    
    // Cleanup
    g_bus_unown_name(owner_id);
    g_dbus_connection_unregister_object(connection, registration_id);
    g_dbus_node_info_unref(introspection_data);
    g_object_unref(connection);
    g_main_loop_unref(loop);

    return 0;
}

static void handle_method_call(GDBusConnection *connection,
                               const gchar *sender,
                               const gchar *object_path,
                               const gchar *interface_name,
                               const gchar *method_name,
                               GVariant *parameters,
                               GDBusMethodInvocation *invocation,
                               gpointer user_data)
{
    g_print("Received method call: %s from %s\n", method_name, sender);

    if (g_strcmp0(method_name, "SayHello") == 0) {
        const gchar *name;
        g_variant_get(parameters, "(&s)", &name);

        gchar *response = g_strdup_printf("Hello, %s!", name);
        g_dbus_method_invocation_return_value(invocation, 
                                             g_variant_new("(s)", response));
        g_free(response);

        g_print("Responded to SayHello method\n");
    } 
    else if (g_strcmp0(method_name, "AddNumbers") == 0) {
        gint32 a, b;
        g_variant_get(parameters, "(ii)", &a, &b);

        gint32 sum = a + b;
        g_dbus_method_invocation_return_value(invocation, 
                                             g_variant_new("(i)", sum));

        g_print("Responded to AddNumbers method: %d + %d = %d\n", a, b, sum);
    } 
    else {
        GError *error = g_error_new(G_DBUS_ERROR, 
                                   G_DBUS_ERROR_UNKNOWN_METHOD,
                                   "Unknown method: %s", method_name);
        g_dbus_method_invocation_return_gerror(invocation, error);
        g_error_free(error);
        
        g_print("Rejected unknown method: %s\n", method_name);
    }
}

static gboolean emit_signal(gpointer user_data)
{
    static guint counter = 0;
    GDBusConnection *connection = (GDBusConnection *)user_data;
    GError *error = NULL;
    
    GVariant *signal_value = g_variant_new("(s)", 
        g_strdup_printf("Service is alive! Count: %d", ++counter));
    
    g_dbus_connection_emit_signal(connection,
                                 NULL,
                                 EXAMPLE_OBJECT_PATH,
                                 introspection_data->interfaces[0]->name,
                                 "ServiceSignal",
                                 signal_value,
                                 &error);
    
    if (error != NULL) {
        g_printerr("Error emitting signal: %s\n", error->message);
        g_error_free(error);
    } else {
        g_print("Emitted signal: Service is alive! Count: %d\n", counter);
    }
    
    return G_SOURCE_CONTINUE; // Continue the timer
}
